﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using DynORM.Interfaces;
using DynORM.Mappers;
using DynORM.Enums;
using DynORM;
using DynORM.ValueObjects;
using DynORM.ReferenceObjects;

namespace UnitTests.DAL
{
    [TestClass]
    public class PostgreSQLDALUnitTests
    {
        static string dbName = "TestDB";
        static string pooling = "Pooling=false;";//otherwise it won't let drop db (db will be in use with connections in pool)
        static string connectionStringToDatabase = "Server=127.0.0.1;Port=5432;User Id=postgres;Password=postgres;Database=" + dbName + ";" + pooling;
        static string connectionStringToServer = "Server=127.0.0.1;Port=5432;User Id=postgres;Password=postgres;" + pooling;

        [TestInitialize]
        public void TestInit()
        {
            DatabaseMapper.CreateDatabase(connectionStringToServer, DBMSTypes.PostgreSQL, new Database(dbName));
        }

        [TestCleanup]
        public void TestClean()
        {
            DatabaseMapper.DropDatabase(connectionStringToServer, DBMSTypes.PostgreSQL, dbName);
        }


        void CreateNewRowDescriptor(string tableName, DBMSTypes dbmsTypes)
        {
            Dictionary<int, IFieldDescriptor> pkFieldDescs = new Dictionary<int, IFieldDescriptor>();
            pkFieldDescs.Add(0, new FieldDescriptor(typeof(Guid), "RowID"));
            PrimaryKeyDescriptor pkd = new PrimaryKeyDescriptor(tableName, pkFieldDescs, tableName + "_PK");
            List<FieldDescriptor> fieldDescs = new List<FieldDescriptor>();
            fieldDescs.Add(new FieldDescriptor(typeof(string), "TestColumn"));
            RowDescriptor rd = new RowDescriptor(tableName, fieldDescs, new List<IRelationDescriptor>() { pkd });
            RowDescriptorMapper.Store(connectionStringToDatabase, dbmsTypes, rd);
        }

        void DropRowDescriptor(string tableName, DBMSTypes dbmsTypes)
        {
            RowDescriptorMapper.Drop(connectionStringToDatabase, dbmsTypes, tableName);
        }

        [TestMethod]
        public void CreateNewRowDescriptorTest()
        {
            string tableName = "TestTable";
            CreateNewRowDescriptor(tableName, DBMSTypes.PostgreSQL);
            IRowDescriptor rd = RowDescriptorMapper.Retrieve(connectionStringToDatabase, DBMSTypes.PostgreSQL, tableName);
            Assert.AreEqual(tableName, rd.RelationDescriptors.First<IRelationDescriptor>(r => r.RelationType == RelationType.PrimaryKey).TableName);
            DropRowDescriptor(tableName, DBMSTypes.PostgreSQL);
        }

        [TestMethod]
        public void DropRowDescriptorTest()
        {
            string tableName = "TestTable";
            CreateNewRowDescriptor(tableName, DBMSTypes.PostgreSQL);
            DropRowDescriptor(tableName, DBMSTypes.PostgreSQL);
            IRowDescriptor rd = RowDescriptorMapper.Retrieve(connectionStringToDatabase, DBMSTypes.PostgreSQL, tableName);
            Assert.IsNull(rd);
        }

        [TestMethod]
        public void RowDescriptorRetrieveTest()
        {
            string tableName = "TestTable";
            CreateNewRowDescriptor(tableName, DBMSTypes.PostgreSQL);
            IRowDescriptor rd = RowDescriptorMapper.Retrieve(connectionStringToDatabase, DBMSTypes.PostgreSQL, tableName);
            Assert.AreEqual(tableName, rd.RelationDescriptors.First<IRelationDescriptor>(r => r.RelationType == RelationType.PrimaryKey).TableName);
            DropRowDescriptor(tableName, DBMSTypes.PostgreSQL);
        }

        [TestMethod]
        public void ChangeStructureTest()
        {
            string tableName = "TestTable";
            CreateNewRowDescriptor(tableName, DBMSTypes.PostgreSQL);

            IRowDescriptor rd = RowDescriptorMapper.Retrieve(connectionStringToDatabase, DBMSTypes.PostgreSQL, tableName);
            Assert.AreEqual(tableName, rd.RelationDescriptors.First<IRelationDescriptor>(r => r.RelationType == RelationType.PrimaryKey).TableName);
            rd.AddField(new FieldDescriptor(typeof(int?), "TestColumn1"));
            RowDescriptorMapper.Store(connectionStringToDatabase, DBMSTypes.PostgreSQL, rd);

            rd = RowDescriptorMapper.Retrieve(connectionStringToDatabase, DBMSTypes.PostgreSQL, tableName);
            bool flag = false;
            foreach (IFieldDescriptor fd in rd.FieldDescriptors)
                if (String.Compare(fd.FieldName, "TestColumn1", StringComparison.OrdinalIgnoreCase) == 0)
                    flag = true;

            if (flag == false) Assert.Fail();

            DropRowDescriptor(tableName, DBMSTypes.PostgreSQL);
        }

        void AddRow(string tableName, DBMSTypes dbmsTypes, Guid id)
        {
            IRowDescriptor rd = RowDescriptorMapper.Retrieve(connectionStringToDatabase, dbmsTypes, tableName);
            Assert.AreEqual(tableName, rd.RelationDescriptors.First<IRelationDescriptor>(r => r.RelationType == RelationType.PrimaryKey).TableName);
            Row row = new Row(rd, id, "test");
            //row["TestColumn"] = "some test entity name";
            RowMapper.RowWrite(connectionStringToDatabase, dbmsTypes, row);
        }

        [TestMethod]
        public void AddRowTest()
        {
            string tableName = "TestTable";
            CreateNewRowDescriptor(tableName, DBMSTypes.PostgreSQL);
            Guid id = Guid.NewGuid();
            AddRow(tableName, DBMSTypes.PostgreSQL, id);

            Dictionary<int, IField> fs = new Dictionary<int, IField>();
            fs.Add(0, new Field(typeof(Guid), "RowID", id));
            IPrimaryKey pk = new PrimaryKey(tableName, fs, tableName + "_PK");
            IRow row = RowMapper.RowRead(connectionStringToDatabase, DBMSTypes.PostgreSQL, pk);
            Assert.IsNotNull(row);

            DropRowDescriptor(tableName, DBMSTypes.PostgreSQL);
        }

        void AddEmptyRow(string tableName, DBMSTypes dbmsTypes, Guid id)
        {
            IRowDescriptor rd = RowDescriptorMapper.Retrieve(connectionStringToDatabase, dbmsTypes, tableName);
            Assert.AreEqual(tableName, rd.RelationDescriptors.First<IRelationDescriptor>(r => r.RelationType == RelationType.PrimaryKey).TableName);
            IPrimaryKey pk = new PrimaryKey(rd.PrimaryKeyDescriptor, id);
            Row row = new Row(pk, rd);
            //row["TestColumn"] = "some test entity name";
            RowMapper.RowWrite(connectionStringToDatabase, dbmsTypes, row);
        }

        [TestMethod]
        public void AddEmptyRowTest()
        {
            string tableName = "TestTable";
            CreateNewRowDescriptor(tableName, DBMSTypes.PostgreSQL);
            Guid id = Guid.NewGuid();
            AddEmptyRow(tableName, DBMSTypes.PostgreSQL, id);

            Dictionary<int, IField> fs = new Dictionary<int, IField>();
            fs.Add(0, new Field(typeof(Guid), "RowID", id));
            IPrimaryKey pk = new PrimaryKey(tableName, fs, tableName + "_PK");
            IRow row = RowMapper.RowRead(connectionStringToDatabase, DBMSTypes.PostgreSQL, pk);
            Assert.IsNotNull(row);

            DropRowDescriptor(tableName, DBMSTypes.PostgreSQL);
        }

        [TestMethod]
        public void SelectRowTest()
        {
            string tableName = "TestTable";
            CreateNewRowDescriptor(tableName, DBMSTypes.PostgreSQL);
            Guid id = Guid.NewGuid();
            AddRow(tableName, DBMSTypes.PostgreSQL, id);

            Field field = new Field(typeof(Guid), "RowID", id);
            Dictionary<int, IField> fs = new Dictionary<int, IField>();
            fs.Add(0, field);
            PrimaryKey pk = new PrimaryKey(tableName, fs, tableName + "_PK");
            IRow readRow = RowMapper.RowRead(connectionStringToDatabase, DBMSTypes.PostgreSQL, pk);
            //can access fields like this...
            Assert.AreEqual<Guid>(id, (Guid)readRow.Relations.First<IRelation>(r => r.RelationType == RelationType.PrimaryKey).Fields.First<KeyValuePair<int, IField>>().Value.FieldValue);
            Assert.AreEqual("test", (string)readRow.Fields.First<IField>(f => String.Compare(f.FieldName, "TestColumn", false) == 0).FieldValue);
            //...or like this
            Assert.AreEqual("test", (string)readRow["TestColumn"]);

            DropRowDescriptor(tableName, DBMSTypes.PostgreSQL);
        }

        [TestMethod]
        public void DeleteRowTest()
        {
            string tableName = "TestTable";
            CreateNewRowDescriptor(tableName, DBMSTypes.PostgreSQL);
            Guid id = Guid.NewGuid();
            AddRow(tableName, DBMSTypes.PostgreSQL, id);

            Dictionary<int, IField> fs = new Dictionary<int, IField>();
            fs.Add(0, new Field(typeof(Guid), "RowID", id));
            IPrimaryKey pk = new PrimaryKey(tableName, fs, tableName + "_PK");
            RowMapper.RowDelete(connectionStringToDatabase, DBMSTypes.PostgreSQL, pk);

            IRow row = RowMapper.RowRead(connectionStringToDatabase, DBMSTypes.PostgreSQL, pk);
            Assert.IsNull(row);

            DropRowDescriptor(tableName, DBMSTypes.PostgreSQL);
        }

        [TestMethod]
        public void NonStoredRowDescriptorRetrieveTest()
        {
            IRowDescriptor rd = RowDescriptorMapper.Retrieve(connectionStringToDatabase, DBMSTypes.PostgreSQL, "NotExists");
            Assert.IsNull(rd);
        }

        [TestMethod]
        public void SelectNonExistRowTest()
        {
            Field field = new Field(typeof(Guid), "RowID", Guid.Empty);
            Dictionary<int, IField> fs = new Dictionary<int, IField>();
            fs.Add(0, field);
            PrimaryKey pk = new PrimaryKey("Entities", fs, "Entities_PK");
            IRow readRow = RowMapper.RowRead(connectionStringToDatabase, DBMSTypes.PostgreSQL, pk);
            Assert.IsNull(readRow);
        }


    }
}
